# -*- coding: UTF-8 -*-
from Code.Utils.sc_func import Common
from Code.Model.sc_global import Global
from Code.Model.Data.sc_ssh import SSH, SSHUtil
from Code.Model.Data.sc_data import model_gate, wrapper_exception
from Code.Model.Controller.sc_loader import loader
from Code.Model.Controller.sc_logger import logger
from Code.Model.Controller.sc_timer import timer_handler

language_data = model_gate.query_language_data


class LoginHandler:
    """ 登录处理类 """

    @classmethod
    def login_server(cls, ip_data, pack_path):
        """ 登录服务器操作，服务器必须要满足以下条件才能正常登录和使用
            1. 具备基本的bash命令，如cd/rm/mkdir/chmod等
            2. 存在tar压缩解压命令
            3. python版本>=2.6
            4. Linux kernel版本>=2.6
        """
        def ssh_login():
            ret, err = SSHUtil.user_login(ssh, user)
            if not ret:
                raise Exception(err)
            ret, err = SSHUtil.user_login(ssh, 'root')
            if not ret:
                raise Exception(err)

        def init_server():
            # 如果上次登录用户跟这次不一致，会导致后面解压失败; 这里每次登录都清空目录
            cmd = "rm -rf {0}/*; mkdir {0}; chmod 777 {0}".format(server_dir)
            SSHUtil.exec_ret(ssh, cmd, root=True)

        def upload_package():
            remote_path = "{}/{}".format(server_dir, Global.G_PACK_ZIP)
            unzip_cmd = "cd {0} && tar zxf {1} && chmod 777 {0}/*".format(server_dir, Global.G_PACK_ZIP)
            ret, err = SSHUtil.upload_file(ssh, pack_path, remote_path)
            if not ret:
                raise Exception(err)
            ret, err = SSHUtil.exec_ret(ssh, unzip_cmd, root=True)
            if not ret:
                raise Exception(err)

        def post_handle():
            """ 初始化并检查python环境 """
            init_env_cmd = "{}/{} check_env".format(server_dir, Global.G_FUNC_CALLER)
            ret, err = SSHUtil.exec_ret(ssh, init_env_cmd, root=True)
            if not ret:
                raise Exception(err)

        def update_login_data(state):
            login_state_data[ip] = {
                'PWD': [user, upwd, rpwd],
                'SSH': ssh,
                'STATE': state
            }
            model_gate.login_state_data.set_data(login_state_data)
            Common.mkdir("{}\\{}".format(Global.G_DOWNLOAD_DIR, ip))

        ip, user, upwd, rpwd = ip_data
        port = model_gate.settings_data.sshd_port
        login_state_data = model_gate.login_state_data.get_data()
        retry_times_limit = model_gate.settings_data.retry_times
        server_dir = model_gate.settings_data.server_dir
        for times in range(1, retry_times_limit + 1):
            ssh = SSH(ip, port, user, upwd, rpwd)
            update_login_data('LOGGING')
            try:
                ssh_login()
                init_server()
                upload_package()
                post_handle()
            except Exception as e:
                logger.warn('{} login failed. times: {}, err: {}'.format(ip, times, e))
                if times == retry_times_limit:
                    update_login_data('FAILED')
                    return False, str(e)
                continue
            # login success
            update_login_data('SUCCESS')
            logger.info('{} login success'.format(ip))
            break
        return True, None

    @classmethod
    def init_package(cls):
        zip_file = "{}\\{}".format(Global.G_PID_DIR, Global.G_PACK_ZIP)
        for file in Common.list_dir(Global.G_SCRIPTS_DIR)[2]:
            if file.endswith(".sh"):
                Common.dos2unix('{}/{}'.format(Global.G_SCRIPTS_DIR, file))
        Common.zip_dir(Global.G_SCRIPTS_DIR, zip_file)
        return zip_file

    @classmethod
    def try_login(cls, ip_data_list):
        zip_file = cls.init_package()
        model_gate.login_state_data.set_data({})   # 初始化登录状态数据
        ip, ret, err = None, '', ''
        for index, ip_data in enumerate(ip_data_list):
            ip = ip_data[0]
            model_gate.try_login_return_data.set_data((index, 'LOGGING', '{} {}'.format(ip, language_data('LOGGING'))))
            ret, err = cls.login_server(ip_data, zip_file)
            if ret:
                model_gate.try_login_return_data.set_data((index, 'SUCCESS', None))
            else:
                model_gate.try_login_return_data.set_data((index, 'FAILED', err))
                break   # 有失败则停止并返回
        if ret:
            SettingsHandler.update_prefer_ips()
            timer_handler.start()   # 开启定时器任务 #
            info = language_data('LOGIN', 'SUCCESS')
        else:
            info = '{}{}\n{}'.format(ip, language_data('LOGIN', 'FAILED'), err)
        model_gate.try_login_return_data.set_data(('ALL', ret, info))

    @classmethod
    def new_login(cls, ip_data):
        zip_file = cls.init_package()
        ret, err = cls.login_server(ip_data, zip_file)
        if not ret:
            info = '{}\n{}'.format(language_data('FAILED'), err)
            model_gate.new_login_return_data.set_data((ip_data[0], 'FAILED', info))
            return
        model_gate.new_login_return_data.set_data((ip_data[0], 'SUCCESS', language_data('SUCCESS')))
        SettingsHandler.update_prefer_ips()

    @classmethod
    def del_login(cls, data):
        ip, = data
        login_state_data = model_gate.login_state_data.get_data()
        if ip in login_state_data:
            del login_state_data[ip]
        model_gate.login_state_data.set_data(login_state_data)
        logger.info("delete {} login instance data.".format(ip))


class SettingsHandler:

    @classmethod
    @wrapper_exception
    def update_prefer_ips(cls):
        # 更新常用IP
        if model_gate.settings_data.preference_switch == 'No':
            return
        curr_ips = list(model_gate.login_state_data.get_data().keys())
        last_ips = model_gate.settings_data.preference_ip
        new_ips = last_ips + curr_ips
        new_ips = list(set(new_ips))
        new_ips.sort()
        loader.settings_writer('preference_ip', new_ips)

    @classmethod
    @wrapper_exception
    def update_settings(cls, data):
        for key, value in data.items():
            loader.settings_writer(key, value)
        loader.json_parser(True)

